package com.demo.interceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.util.Collections;

/**
 * 在设计 SDK 的默认限流参数（capacity 和 refillRate）时，需要综合考虑 系统资源承载能力、业务场景 和 用户体验。以下是具体建议和详细分析：
 *
 * 1. 默认参数设定原则
 * 参数	作用	设定原则
 * capacity	令牌桶容量，控制突发流量上限	允许合理的突发请求（如秒级突发），但避免因突发流量耗尽系统资源。
 * refillRate	令牌补充速率，控制平均请求速率	根据系统处理能力和业务场景，设定可持续的平均请求速率，避免长期高负载。
 * 2. 推荐默认值
 * 2.1 通用场景
 * 参数	推荐值	说明
 * capacity	100	允许突发处理 100 个请求（例如：1 秒内突发 100 次请求）。
 * refillRate	10	每秒补充 10 个令牌（即平均每秒允许 10 次请求）。
 * 适用场景：
 *
 * 中小型 API 服务、企业内部系统、低频第三方集成。
 *
 * 需要平衡突发流量和长期稳定性。
 *
 * 2.2 高并发场景
 * 参数	推荐值	说明
 * capacity	1000	允许突发处理 1000 个请求（例如：1 秒内突发 1000 次请求）。
 * refillRate	100	每秒补充 100 个令牌（即平均每秒允许 100 次请求）。
 * 适用场景：
 *
 * 高并发 API 服务、开放平台（如电商、社交类应用）。
 *
 * 需要支持高吞吐量，但需避免恶意流量。
 *
 * 3. 参数调整建议
 * 3.1 根据系统资源调整
 * 系统资源能力	capacity	refillRate	说明
 * 低配服务器	50	5	单核 CPU、2GB 内存，适用于小型服务。
 * 中配服务器	200	20	4 核 CPU、8GB 内存，适用于中等负载服务。
 * 高配服务器	500+	50+	分布式集群、高性能硬件，适用于高并发场景。
 * 3.2 根据业务场景调整
 * 业务场景	capacity	refillRate	说明
 * 低频操作	50	5	如登录、支付等敏感操作，需严格限流。
 * 高频查询	500	50	如数据查询、列表加载，允许较高并发。
 * 实时通信	1000	100	如消息推送、WebSocket，需支持高吞吐量。
 * 4. 动态调整策略
 * 4.1 自适应限流
 * 动态监控：根据实时流量和系统负载（如 CPU、内存、响应时间）动态调整 capacity 和 refillRate。
 *
 * 示例：
 *
 * java
 * 复制
 * // 伪代码：根据系统负载调整限流参数
 * if (systemLoad > 80%) {
 *     refillRate = refillRate * 0.8; // 负载高时降低速率
 * } else {
 *     refillRate = defaultRefillRate; // 恢复默认值
 * }
 * 4.2 按租户分级
 * SLA 分级：根据租户的付费等级或合同约定（SLA），设置不同的限流参数。
 *
 * java
 * 复制
 * // 伪代码：按付费等级设置限流
 * if (userPlan == "basic") {
 *     capacity = 100;
 *     refillRate = 10;
 * } else if (userPlan == "premium") {
 *     capacity = 1000;
 *     refillRate = 100;
 * }
 *
 * 5. 注意事项
 * 监控与告警
 *
 * 集成监控系统（如 Prometheus、ELK）实时跟踪限流触发情况。
 *
 * 当限流拒绝率超过阈值时触发告警。
 *
 * 文档透明化
 *
 * 在 SDK 文档中明确说明默认限流值及其影响。
 *
 * 提供参数调整指南，帮助开发者根据业务需求优化配置。
 *
 * 灰度发布
 *
 * 新版本 SDK 调整限流参数时，通过灰度发布逐步验证效果。
 */
@Service
public class RedisRateLimiter {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 尝试获取令牌
     *
     * @param appId      应用 ID
     * @param capacity   令牌桶容量，控制突发流量上限	允许合理的突发请求（如秒级突发），但避免因突发流量耗尽系统资源。
     * @param refillRate 令牌补充速率（每秒）控制平均请求速率	根据系统处理能力和业务场景，设定可持续的平均请求速率，
     * @return true：允许请求；false：拒绝请求
     */
    public boolean tryAcquire(String appId, int capacity, int refillRate) {
        String key = "rate_limit:" + appId;
        // 当前时间（秒）
        long now = System.currentTimeMillis() / 1000;

        // Lua 脚本
        String luaScript = 
            "local key = KEYS[1]\n" +
            "local capacity = tonumber(ARGV[1])\n" +
            "local refillRate = tonumber(ARGV[2])\n" +
            "local now = tonumber(ARGV[3])\n" +
            "local currentTokens = tonumber(redis.call('get', key)) or capacity\n" +
            "local lastRefillTime = tonumber(redis.call('get', key .. ':last_refill')) or now\n" +
            "local timePassed = now - lastRefillTime\n" +
            "local tokensToAdd = timePassed * refillRate\n" +
            "local newTokens = math.min(currentTokens + tokensToAdd, capacity)\n" +
            "redis.call('set', key, newTokens)\n" +
            "redis.call('set', key .. ':last_refill', now)\n" +
            "if newTokens >= 1 then\n" +
            "    redis.call('decr', key)\n" +
            "    return 1\n" +
            "else\n" +
            "    return 0\n" +
            "end";

        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText(luaScript);
        script.setResultType(Long.class);

        // 执行脚本
        Long result = redisTemplate.execute(script, 
            Collections.singletonList(key), 
            String.valueOf(capacity), 
            String.valueOf(refillRate), 
            String.valueOf(now));

        return result == 1;
    }


    /**
     * 限流脚本
     */
    private String limitScriptText()
    {
        return "local key = KEYS[1] -- 限流键（如 rate_limit:appId）\n" +
                "local capacity = tonumber(ARGV[1]) -- 桶的容量\n" +
                "local refillRate = tonumber(ARGV[2]) -- 令牌补充速率（每秒）\n" +
                "local now = tonumber(ARGV[3]) -- 当前时间戳（秒）\n" +
                "\n" +
                "-- 获取当前令牌数量和上次补充时间\n" +
                "local currentTokens = tonumber(redis.call('get', key) or capacity\n" +
                "local lastRefillTime = tonumber(redis.call('get', key .. ':last_refill') or now\n" +
                "\n" +
                "-- 计算时间差（秒）\n" +
                "local timePassed = now - lastRefillTime\n" +
                "\n" +
                "-- 计算应补充的令牌数量\n" +
                "local tokensToAdd = timePassed * refillRate\n" +
                "local newTokens = math.min(currentTokens + tokensToAdd, capacity)\n" +
                "\n" +
                "-- 更新令牌数量和上次补充时间\n" +
                "redis.call('set', key, newTokens)\n" +
                "redis.call('set', key .. ':last_refill', now)\n" +
                "\n" +
                "-- 检查是否有足够的令牌\n" +
                "if newTokens >= 1 then\n" +
                "    redis.call('decr', key)\n" +
                "    return 1 -- 允许请求\n" +
                "else\n" +
                "    return 0 -- 拒绝请求\n" +
                "end";
    }
}